using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using Zsa.Emulation.Config;
using Zsa.Emulation.Util;

namespace Zsa.Emulation
{
    /// <summary>
    /// The nes machine.
    /// </summary>
    public class NesMachine
    {
        #region Fields

        #region Emulated Hardware
        /// <summary>
        /// The main memory.
        /// </summary>
        private Processor.CpuMemoryMap _MainMemory;
        /// <summary>
        /// The cpu.
        /// </summary>
        private Processor.Cpu6502 _Cpu;
        /// <summary>
        /// The cartiage slot.
        /// </summary>
        private Cartridge.CartridgeSlot _CartIO;
        /// <summary>
        /// The controller io handler.
        /// </summary>
        private Input.ControllerIO _Controller;
        /// <summary>
        /// The picture processing unit.
        /// </summary>
        private PPU.PictureProcessingUnit _PPU;
        /// <summary>
        /// The sound controller.
        /// </summary>
        private Sound.SoundController _SoundController;
        #endregion Emulated Hardware

        /// <summary>
        /// The nes settings.
        /// </summary>
        private NesMachineSettings _Settings;
        /// <summary>
        /// The device state.
        /// </summary>
        private MachineState _DeviceStatus;
        /// <summary>
        /// The timing cookie.
        /// </summary>
        private TimingCookie _TimingCookie;
        /// <summary>
        /// True if pal cart.
        /// </summary>
        private bool _IsPal;
        private static int _NesNTSCClockSpeed = 1789773;
        #endregion Fields

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:NesMachine"/> class.
        /// Using the default settings.
        /// </summary>
        public NesMachine()
            : this ( NesMachineSettings.Default )
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="NesDevice"/> class.
        /// </summary>
        /// <param name="nesSettings">The nes settings.</param>
        public NesMachine(NesMachineSettings nesSettings)
        {
            _DeviceStatus = MachineState.NeverStarted;
            _Settings = nesSettings;
        }
        #endregion Constructors

        #region Methods

        #region Machine State Methods

        /// <summary>
        /// Turns the nes on.
        /// </summary>
        public void Start()
        {
            // If machine never started then reset the machine.
            if (_DeviceStatus == MachineState.NeverStarted)
            {
                Reset();
            }
            // If no cart throw error.
            if (_CartIO.IsEmpty)
            {
                throw new ApplicationException();
            }

            // Init the machine
            Init();
            // Set the status to on.
            _DeviceStatus = MachineState.On;
            // Raise the reset interrupt.
            _Cpu.SetInterrupt(Zsa.Emulation.Processor.InterruptType.Reset);

            _TimingCookie = new TimingCookie(_Settings.GetFramePerMillisecound(_CartIO.CurrentCart.FileInfo));
            TraceLogger.TraceInformation(TraceSubsystem.Cpu,"Loading Game " + _CartIO.CartridgeFileInfo.FileName.ToString());
            TraceLogger.TraceInformation("Loading Game " + _CartIO.CartridgeFileInfo.FileName.ToString());
        }

        /// <summary>
        /// Turns the nes off.
        /// </summary>
        public void Shutdown()
        {
            Reset();
        }

        /// <summary>
        /// Resets the nes and turns the nes on if there is a cartridge inside.
        /// </summary>
        public void Restart()
        {
            if ((_DeviceStatus == MachineState.On ||
                _DeviceStatus == MachineState.Paused) && !_CartIO.IsEmpty)
            {
                // Get the cart.
                Cartridge.Cart _Cart = _CartIO.CurrentCart;
                // Reset the machine.
                Reset();
                // Load the cart.
                _CartIO.LoadCart(_Cart);
                // Set the machine state back to running.
                _DeviceStatus = MachineState.On;
            }
            else
            {
                Reset();
            }
        }

        /// <summary>
        /// Resets all variables and turns the nes off.
        /// </summary>
        public void Reset()
        {
            // Create the cartridge io handler.
            _CartIO = new Cartridge.CartridgeSlot();
            // Create the ppu.
            _PPU = new PPU.PictureProcessingUnit(_CartIO);
            // Create the controller io handler.
            _Controller = new Input.ControllerIO();
            // Create the sound controller.
            _SoundController = new Zsa.Emulation.Sound.SoundController();
            //Create the main memory manager.
            _MainMemory = new Processor.CpuMemoryMap(
                _CartIO, _Controller, _PPU.CpuMemoryMapper,
                _SoundController);
            // Create the processor.
            _Cpu = new Zsa.Emulation.Processor.Cpu6502(_MainMemory);
            // Set the main memory for dma.
            _PPU.CpuMemoryMapper.MainMem = _MainMemory;
            //Set the device status to off.
            _DeviceStatus = MachineState.Off;
        }

        /// <summary>
        /// Run the device enough to render the specified amount of frames.
        /// </summary>
        /// <param name="frames">The number of frame to render.</param>
        public void Run(int frames)
        {
            for (int _FrameIndex = 0; _FrameIndex < frames; _FrameIndex++)
            {
                // Get the cycles need.
                float _Cycles = (113.3333333333F * 240.00F);  // This change if pal
                // The total cycles need for a frame.
                int _TotalCycles = (int)Math.Floor((decimal)_Cycles);
                // The cycles to execute and the cycles executed.
                _Cpu.Execute(_TotalCycles);
                // Set the vblank flag.
                _PPU.InVBlank = true;
                //Execute one cycle.
                _Cpu.Execute(1);
                // If nmi then goto nmi.
                if (Ppu.NmiFlag)
                {
                    _Cpu.SetInterrupt(Zsa.Emulation.Processor.InterruptType.Nmi);
                }
                // Get the total cycles.
                _TotalCycles = (int)(frames * (113.333D * 23D));
                // Execute them.
                _Cpu.Execute(_TotalCycles);
                _PPU.InVBlank = false;
            }
            
            //return _TimingCookie.Update(frames);
        }

        #region Pause
        /// <summary>
        /// Pauses this instance.
        /// </summary>
        public void Pause()
        {
            PauseResume(true);
        }

        /// <summary>
        /// Resumes this instance.
        /// </summary>
        public void Resume()
        {
            PauseResume(false);
        }

        /// <summary>
        /// Pauses the resume.
        /// </summary>
        public void PauseResume()
        {
            PauseResume(_DeviceStatus == MachineState.On);
        }

        /// <summary>
        /// Pauses this instance.
        /// </summary>
        public void PauseResume(bool shouldPause)
        {
            if ( shouldPause && _DeviceStatus == MachineState.On)
            {
                _DeviceStatus = MachineState.Paused;
            }
            else if ( !shouldPause && _DeviceStatus == MachineState.Paused )
            {
                _DeviceStatus = MachineState.On;
            }
        }
        #endregion Pause

        /// <summary>
        /// Initializes the nes member variables.
        /// Some of them can be expensive.
        /// </summary>
        private void Init()
        {
            _CartIO.Init();
            _MainMemory.Init();
            _PPU.Init();
            _Cpu.ClearRegisters();
        }

        #endregion Machine State Methods

        /// <summary>
        /// Saves the state of a game.
        /// </summary>
        /// <returns></returns>
        public Games.SaveStateData SaveState()
        {
            return new Zsa.Emulation.Games.SaveStateData();
        }

        #endregion Methods

        #region Properties

        /// <summary>
        /// Gets the cpu.
        /// </summary>
        /// <value>The cpu.</value>
        public Processor.Cpu6502 Cpu
        {
            get { return _Cpu; }
        }
        public Input.ControllerIO Controller
        {
            get
            {
                return _Controller;
            }
        }
        /// <summary>
        /// The ppu.
        /// </summary>
        public PPU.PictureProcessingUnit Ppu
        {
            get
            {
                return _PPU;
            }
        }

        /// <summary>
        /// Gets the cartridge slot.
        /// </summary>
        /// <value>The cartridge slot.</value>
        public Cartridge.CartridgeSlot CartridgeSlot
        {
            get { return _CartIO; }
        }



        /// <summary>
        /// Gets the status.
        /// </summary>
        /// <value>The status.</value>
        public MachineState Status
        {
            get
            {
                return _DeviceStatus;
            }
        }

        /// <summary>
        /// Gets the settings.
        /// </summary>
        /// <value>The settings.</value>
        public NesMachineSettings Settings
        {
            get
            {
                return _Settings;
            }
        }
        #endregion Properties

    }
}


